package so.sao.code.service.infrastructure.utils;

import java.lang.invoke.MethodHandles;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import so.sao.tag.core.TagCode;

public class CustomDefinitionParseUtils {

	private static Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

	private static final String URL = "\\{URL\\}";

	private static final String DOMAIN = "\\{DOMAIN\\}";

	private static final String SUBDOMAIN = "\\{SUBDOMAIN\\}";

	private static final String CODE = "\\{CODE\\}";

	private static final String MIXED16 = "\\{MIXED16\\}";

	private static final String MIXED8_1 = "\\{MIXED8-1\\}";

	private static final String MIXED8_2 = "\\{MIXED8-2\\}";

	private static final String SEQ = "\\{SEQ-([1-9][0-9]?)\\}";

	private static final String SEQ_1 = "\\{SEQ-([1-9][0-9]?)-1\\}";

	private static final String SEQ_2 = "\\{SEQ-([1-9][0-9]?)-2\\}";

	private static final String[] definitions = { URL, DOMAIN, SUBDOMAIN, CODE, MIXED16, MIXED8_1, MIXED8_2, SEQ, SEQ_1,
			SEQ_2 };
	/**
	 * 可重用的共享模式Map
	 */
	private static Map<String, Pattern> cachePatterns;

	private static ObjectMapper mapper = new ObjectMapper();

	// {url}+{MIXED8-1}+{SEQ-15-1}
	public static String parseCustomField(String origin, TagCode tagCode) {

		try {
//			tagCode.setCode(tagCode.getCode().replaceAll("$", "\\$"));
			tagCode.setCode(quoteReplacement(tagCode.getCode()));
			if (cachePatterns == null) {
				cachePatterns = new HashMap<String, Pattern>();
				Pattern item = null;
				for (String definition : definitions) {
					item = Pattern.compile("(" + definition + ")");
					cachePatterns.put(definition, item);
				}
			}

			// Pattern pattern = Pattern.compile("{url}+{MIXED8-1}+{SEQ-15-1}");
			Matcher matcher;
			Set<String> set = cachePatterns.keySet();
			String replacement = "";
			String seqNumStr = "";
			long seq = tagCode.getSeqNum();
			for (String key : set) {
				Pattern temp = (Pattern) cachePatterns.get(key);
				System.out.println(origin);
				switch (key) {
				case URL:
					replacement = tagCode.getDomain() + tagCode.getSubDomain() + tagCode.getCode();
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case DOMAIN:
					replacement = tagCode.getDomain();
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case SUBDOMAIN:
					replacement = tagCode.getSubDomain();
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case CODE:
					replacement = tagCode.getCode();
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case MIXED16:
					replacement = tagCode.getMixed();
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case MIXED8_1:
					replacement = tagCode.getMixed().substring(0, 8);
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case MIXED8_2:
					replacement = tagCode.getMixed().substring(8);
					matcher = temp.matcher(origin);
					origin = matcher.replaceAll(replacement);
					break;
				case SEQ:
					matcher = temp.matcher(origin);
					int requiredSeqLen = 0;
					int realLen = 0;
					String realSeqStr = "";
					seqNumStr = String.valueOf(seq);
					int index = 0;
					String group2 = "";
					String group = "";
					while (matcher.find(index)) {
						group = matcher.group();
						group = group.substring(1, group.length() - 1);
						group2 = matcher.group(2);
						requiredSeqLen = Integer.valueOf(group2);
						realLen = seqNumStr.length();
						realSeqStr = realLen >= requiredSeqLen ? seqNumStr.substring(realLen - requiredSeqLen)
								: appendPaddingStr(requiredSeqLen - realLen) + seqNumStr;
						origin = origin.replaceAll("\\{" + group + "\\}", realSeqStr);
						index = matcher.end();
					}
					break;
				case SEQ_1:
					matcher = temp.matcher(origin);
					origin = splitHalfSeq(seq, matcher, origin, true);
					break;
				case SEQ_2:
					matcher = temp.matcher(origin);
					origin = splitHalfSeq(seq, matcher, origin, false);
					break;
				default:
					break;
				}
			}
			;

		} catch (Exception e) {
			String code = "";
			try {
				code = mapper.writeValueAsString(tagCode);
			} catch (JsonProcessingException e1) {
			}
			log.error(String.format("解析普通模式语义出错,common:%s,码:%s", origin, code), e);
		}
		return origin;
	}

	private static String appendPaddingStr(int count) {
		if (count <= 0) {
			return "";
		}
		StringBuilder sBuilder = new StringBuilder();
		for (int i = 0; i < count; i++) {
			sBuilder.append("0");
		}
		return sBuilder.toString();
	}

	/**
	 * true first false second
	 * 
	 * @param seqNum
	 * @param matcher
	 * @param firstOrSecond
	 */
	private static String splitHalfSeq(long seqNum, Matcher matcher, String result, boolean firstOrSecond) {
		int requiredSeqLen = 0;
		int seqLen = String.valueOf(seqNum).length();
		String realSeqStr = "";
		int index = 0;
		String group = "";
		while (matcher.find(index)) {
			group = matcher.group();
			group = group.substring(1, group.length() - 1);
			requiredSeqLen = Integer.valueOf(matcher.group(2));
			realSeqStr = String.valueOf(seqNum);

			realSeqStr = seqLen >= requiredSeqLen ? realSeqStr.substring(seqLen - requiredSeqLen)
					: appendPaddingStr(requiredSeqLen - seqLen) + realSeqStr;

			int realLen = realSeqStr.length();
			if (realLen % 2 == 0) {
				if (firstOrSecond) {
					realSeqStr = realSeqStr.substring(0, realLen / 2);
					result = result.replaceAll("\\{" + group + "\\}", realSeqStr);
					index = matcher.end();
					continue;
				}
			} else {
				if (firstOrSecond) {
					realSeqStr = appendPaddingStr(1) + realSeqStr.substring(0, realLen / 2);
					result = result.replaceAll("\\{" + group + "\\}", realSeqStr);
					index = matcher.end();
					continue;
				}
			}
			realSeqStr = realSeqStr.substring(realLen / 2);
			result = result.replaceAll("\\{" + group + "\\}", realSeqStr);
			index = matcher.end();
		}
		return result;
	}

	public static String quoteReplacement(String s) {
		if (s.indexOf('$') == -1)
			return s;
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			 if (c == '$') {
				sb.append('\\');
				sb.append('$');
			} else {
				sb.append(c);
			}
		}
		return sb.toString();
	}

}
